//
//  MultiChannTestProc_many.cpp
//  GPUProcessingTesting
//
//  Created by kadlubek47 on 17.06.2013.
//  Copyright (c) 2013 kadlubek47. All rights reserved.
//

#include "MultiChannTestProc_many.h"



#pragma mark Create, Destroy

const char MultiChannTestProc_many::kernelPath_[] = "/Users/kadlubeczek47/programowanie/Praca Inzynierska/parallel-computing-sound-signals_svn_tmp/trunk/GPUProcessingTesting/GPUProcessingTesting/simply_kernels.cl";

MultiChannTestProc_many::MultiChannTestProc_many(): OpenCLProcessorBase((char*)kernelPath_) {
    createKernels();
}

MultiChannTestProc_many::~MultiChannTestProc_many() {
    if(!buffersIn_.empty()) {
        for(int i = 0; i < buffersIn_.size(); ++i ) {
            clReleaseMemObject(buffersIn_[i]);
            clReleaseMemObject(buffersOut_[i]);
        }
        buffersIn_.clear();
        buffersOut_.clear();
    }
    
    clReleaseKernel(inOutKernel_);
}

void MultiChannTestProc_many::createKernels() {
    inOutKernel_ = clCreateKernel(program_, "in_out", &lastCommandStatus_);
}



#pragma mark manyChannel main_function

void MultiChannTestProc_many::process(float** channels, unsigned int channelsNum, unsigned int channelLength, float** destiny, double* timeOfCallCommandsFun, double* timeOfFlushAndFinish) {
    SimplyTimer timer;
    
    //Calling commands functions
    timer.start();
    
    if( (channelsNum != channelsNum_) || (channelLength != channelLength_) ) {
        channelsNum_    = channelsNum;
        channelLength_  = channelLength;
        createBuffersForChannels();
    }
    
    loadBlocksOfChannelsIntoGPU(channels);
    executeKernel();
    getBlocksOfChannelsFromGPU(destiny);
    
    timer.stop();
    *timeOfCallCommandsFun += timer.getTime();
    //.
    
    //Flushing and Finishing
    timer.start();
    
    clFlush(cmdQueue_);
    clFinish(cmdQueue_);
    
    timer.stop();
    *timeOfFlushAndFinish += timer.getTime();
    //.
}



#pragma mark manyChannel subordinate_functions

void MultiChannTestProc_many::createBuffersForChannels() {
    if(!buffersIn_.empty()) {
        for(int i = 0; i < buffersIn_.size(); ++i ) {
            clReleaseMemObject(buffersIn_[i]);
            clReleaseMemObject(buffersOut_[i]);
        }
        buffersIn_.clear();
        buffersOut_.clear();
    }
    
    for (int j = 0; j < 2; ++j)
        for (int i = 0; i < channelsNum_; ++i) {
            cl_mem buffer = clCreateBuffer( context_, CL_MEM_READ_WRITE, channelLength_ * sizeof(float), NULL, &lastCommandStatus_);
            if(j==0)
                buffersIn_.push_back(buffer);
            if(j==1)
                buffersOut_.push_back(buffer);
        }
}

void MultiChannTestProc_many::loadBlocksOfChannelsIntoGPU(float** channels) {
    for (int i = 0; i < channelsNum_; ++i)
        lastCommandStatus_ = clEnqueueWriteBuffer( cmdQueue_, buffersIn_[i], CL_FALSE, 0, channelLength_ * sizeof(float), channels[i], 0, NULL, NULL);
}

void MultiChannTestProc_many::executeKernel() {
    size_t globalWorkSize[1];
    globalWorkSize[0] = channelLength_;
    
    for (int i = 0; i < channelsNum_; ++i) {
        lastCommandStatus_  = clSetKernelArg( inOutKernel_, 0, sizeof(cl_mem), &buffersIn_[i]);
        lastCommandStatus_ |= clSetKernelArg( inOutKernel_, 1, sizeof(cl_mem), &buffersOut_[i]);
        
        lastCommandStatus_ = clEnqueueNDRangeKernel( cmdQueue_, inOutKernel_, 1, NULL, globalWorkSize, NULL, 0, NULL, NULL);
    }
    
}

void MultiChannTestProc_many::getBlocksOfChannelsFromGPU (float** destiny) {
    for (int i = 0; i < channelsNum_; ++i) {
        lastCommandStatus_ = clEnqueueReadBuffer( cmdQueue_, buffersOut_[i], CL_FALSE, 0, channelLength_ * sizeof(float), destiny[i], 0, NULL, NULL);
    }
}